import sys
import os
import datetime
import threading
import time
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

class OutputCapture:
    """标准输出捕获器，完全独立的捕获功能类"""
    def __init__(self, text_widget=None, log_file=None):
        self.text_widget = text_widget  # 显示控件引用
        self.log_file = log_file  # 日志文件路径
        self.original_stdout = sys.stdout # 原始stdout，用于恢复
        self.original_stderr = sys.stderr # 原始stderr，用于恢复
        self.is_capturing = False
        self.lock = threading.Lock()
        
        # 确保日志目录存在
        if self.log_file:
            log_dir = os.path.dirname(self.log_file)
            if log_dir and not os.path.exists(log_dir):
                os.makedirs(log_dir, exist_ok=True)
    
    def write(self, text):
        """重定向输出时的写入方法"""
        with self.lock:
            # 始终保留原始输出，如果不需要输出到终端，可以注释掉
            # self.original_stdout.write(text)
            
            # 写入GUI控件
            if self.text_widget:
                # after(ms, func)：在主线程中安全执行UI更新
                # 参数1: 0 - 立即执行（等待事件循环空闲时）
                # 参数2: lambda函数 - 封装UI更新操作
                self.text_widget.after(0, lambda: self._append_to_text_widget(text))
            
            # 写入日志文件
            if self.log_file:
                try:
                    with open(self.log_file, 'a', encoding='utf-8') as f:
                        f.write(text)
                except Exception as e:
                    # 错误信息只输出到原始stderr，避免循环调用
                    self.original_stderr.write(f"日志写入失败: {e}\n")
    
    def _append_to_text_widget(self, text):
        """安全地向文本控件添加内容"""
        try:
            self.text_widget.insert(END, text)
            self.text_widget.see(END)  # 滚动到底部
        except Exception:
            pass  # 忽略UI更新错误
    
    def flush(self):
        """实现flush方法以保持文件对象兼容性"""
        self.original_stdout.flush()
    
    def start_capture(self):
        """开始捕获标准输出"""
        with self.lock:
            if not self.is_capturing:
                sys.stdout = self
                sys.stderr = self
                self.is_capturing = True
                return True
            return False
    
    def stop_capture(self):
        """停止捕获并恢复标准输出"""
        with self.lock:
            if self.is_capturing:
                sys.stdout = self.original_stdout
                sys.stderr = self.original_stderr
                self.is_capturing = False
                return True
            return False
    
    def log_message(self, message):
        """直接记录消息到日志文件"""
        if self.log_file:
            timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            try:
                with open(self.log_file, 'a', encoding='utf-8') as f:
                    f.write(f"[{timestamp}] {message}\n")
            except Exception:
                pass

class OutputCaptureApp:
    """ttkbootstrap UI界面类，专注于用户界面和交互"""
    def __init__(self, root):
        self.root = root
        self.root.title("Python标准输出捕获工具")
        self.root.geometry("800x600")
        self.root.minsize(600, 400)
        
        # 设置主题样式
        self.style = ttk.Style(theme="cosmo")
        
        # 创建UI
        self._create_ui()
        
        # 创建日志文件
        log_dir = "logs"
        log_filename = f"output_log_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
        self.log_file = os.path.join(log_dir, log_filename)
        
        # 状态栏显示日志文件路径
        self.status_var.set(f"日志文件: {self.log_file}")
        
        # 初始化捕获器，传入文本控件和日志文件路径
        self.capture = OutputCapture(self.text_widget, self.log_file)
        
        # 初始启用捕获
        self._toggle_capture()
    
    def _create_ui(self):
        """创建用户界面"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding=10)
        main_frame.pack(fill=BOTH, expand=YES)
        
        # 创建控制面板
        control_frame = ttk.LabelFrame(main_frame, text="控制面板", padding=10)
        control_frame.pack(fill=X, pady=(0, 10))
        
        # 捕获按钮
        self.capture_var = ttk.BooleanVar(value=True)
        self.capture_button = ttk.Checkbutton(
            control_frame, 
            text="启用输出捕获", 
            variable=self.capture_var, 
            command=self._toggle_capture, 
            bootstyle=SUCCESS
        )
        self.capture_button.pack(side=LEFT, padx=5)
        
        # 清空按钮
        clear_button = ttk.Button(
            control_frame, 
            text="清空输出", 
            command=self._clear_output, 
            bootstyle=WARNING
        )
        clear_button.pack(side=LEFT, padx=5)
        
        # 测试按钮
        test_button = ttk.Button(
            control_frame, 
            text="运行测试输出", 
            command=self._run_test_output, 
            bootstyle=PRIMARY
        )
        test_button.pack(side=LEFT, padx=5)
        
        # 创建输出区域
        output_frame = ttk.LabelFrame(main_frame, text="捕获的输出", padding=10)
        output_frame.pack(fill=BOTH, expand=YES)
        
        # 创建文本控件和滚动条
        self.text_widget = ttk.Text(
            output_frame, 
            wrap=WORD, 
            font=('Consolas', 10),
            bg="#f8f9fa"
        )
        
        # 配置文本控件的tag，用于区分stdout和stderr
        self.text_widget.tag_configure("stdout", foreground="black")
        self.text_widget.tag_configure("stderr", foreground="red")
        
        # 添加垂直滚动条
        y_scroll = ttk.Scrollbar(
            output_frame, 
            orient=VERTICAL, 
            command=self.text_widget.yview
        )
        self.text_widget.configure(yscrollcommand=y_scroll.set)
        
        # 添加水平滚动条
        x_scroll = ttk.Scrollbar(
            output_frame, 
            orient=HORIZONTAL, 
            command=self.text_widget.xview
        )
        self.text_widget.configure(xscrollcommand=x_scroll.set)
        
        # 放置滚动条和文本控件
        y_scroll.pack(side=RIGHT, fill=Y)
        x_scroll.pack(side=BOTTOM, fill=X)
        self.text_widget.pack(fill=BOTH, expand=YES)
        
        # 创建状态栏
        self.status_var = ttk.StringVar()
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=SUNKEN, anchor=W)
        status_bar.pack(side=BOTTOM, fill=X)
    
    def _toggle_capture(self):
        """切换输出捕获状态"""
        if self.capture_var.get():
            if self.capture.start_capture():
                print("已开始捕获标准输出")
                print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 已开始捕获标准输出")
        else:
            if self.capture.stop_capture():
                print("已停止捕获标准输出")
                print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 已停止捕获标准输出")
    
    def _clear_output(self):
        """清空输出文本控件"""
        self.text_widget.delete(1.0, END)
        print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 输出已清空")
    
    def _run_test_output(self):
        """运行测试输出，演示捕获功能"""
        print(f"\n[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 开始测试输出")
        
        # 创建一个新线程来运行测试，避免阻塞UI
        test_thread = threading.Thread(target=self._test_output_thread)
        test_thread.daemon = True  # 设置为守护线程，主线程结束时自动终止
        test_thread.start()
    
    def _test_output_thread(self):
        """在单独线程中运行测试输出"""
        # 模拟正常输出
        print("这是一条标准输出信息 (stdout)")
        time.sleep(0.5)
        
        # 模拟错误输出
        try:
            raise ValueError("这是一个示例异常")
        except Exception as e:
            print(f"错误: {e}", file=sys.stderr)
        
        time.sleep(0.5)
        
        # 模拟复杂输出
        print("\n测试一些特殊字符：中文、!@#$%^&*()")
        time.sleep(0.5)
        
        # 模拟大量输出
        print("\n测试多行输出：")
        for i in range(5):
            print(f"第 {i+1} 行输出 - 包含数字、字母和符号: {i*100 + 123} ABCabc!@#")
            time.sleep(0.2)
        
        print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 测试输出完成\n")

    def on_closing(self):
        """应用程序关闭时的清理操作"""
        # 确保停止捕获并恢复标准输出
        self.capture.stop_capture()
        
        # 使用捕获器的方法记录关闭信息
        self.capture.log_message("应用程序已关闭")
        
        # 关闭窗口
        self.root.destroy()

if __name__ == "__main__":
    # 创建主窗口
    root = ttk.Window(themename="cosmo")
    
    # 创建应用程序实例
    app = OutputCaptureApp(root)
    
    # 设置关闭事件处理
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    
    # 启动主循环
    root.mainloop()